{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4.5. 形态变换\n",
    "\n",
    "### 理论\n",
    "形态学转换是基于图像形状的一些简单操作。它通常在二进制图像上执行。它需要两个输入参数，一个是我们的原始图像，第二个是称为结构元素或核，它决定了操作的性质。腐蚀和膨胀是两个基本的形态学运算符。然后它的变体形式如开运算，闭运算，梯度等也发挥作用。\n",
    "\n",
    "### 腐蚀\n",
    "腐蚀的基本思想就像土壤侵蚀一样，它会腐蚀前景物体的边界（总是试图保持前景为白色）。它是如何做到的呢？卷积核在图像中滑动（如在2D卷积中），只有当卷积核下的所有像素都是1时，原始图像中的像素（1或0）才会被认为是1，否则它会被腐蚀（变为零）。\n",
    "\n",
    "所以腐蚀作用后，边界附近的所有像素都将被丢弃，具体取决于卷积核的大小。因此，前景对象的厚度或大小减小，或者图像中的白色区域减小。它有助于消除小的白噪声（正如我们在色彩空间章节中看到的那样），或者分离两个连接的对象等。\n",
    "\n",
    "在这里，作为一个例子，我将使用一个5x5卷积核，其中包含完整的卷积核。让我们看看它是如何工作的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2 as cv\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "img = cv.imread(\"img/j.png\", 0)\n",
    "\n",
    "kernel = np.ones((5, 5))\n",
    "dis = cv.erode(img, kernel, iterations=1)\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(dis),plt.title(\"dis\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 膨胀\n",
    "它恰好与腐蚀相反。这里，如果卷积核下的像素至少一个像素为“1”，则像素元素为“1”。因此它增加了图像中的白色区域或前景对象的大小。通常，在去除噪音的情况下，侵蚀之后是扩张。因为，侵蚀会消除白噪声，但它也会缩小我们的物体,所以我们扩大它。由于噪音消失了，它们不会再回来，但我们的物体区域会增加。它也可用于连接对象的破碎部分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "dis = cv.dilate(img, kernel, iterations=1)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(dis),plt.title(\"dis\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 开运算\n",
    "开运算是先腐蚀后膨胀的合成步骤。如上所述，它有助于消除噪音。这里我们使用函数cv.morphologyEx()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(opening),plt.title(\"opening\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 闭运算\n",
    "闭运算与开运算相反，他是先膨胀后腐蚀的操作。它可用于过滤前景对象内的小孔或对象上的小黑点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(closing),plt.title(\"closing\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 形态学梯度\n",
    "它的处理结果是显示膨胀和腐蚀之间的差异。\n",
    "结果看起来像对象的轮廓。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(gradient),plt.title(\"gradient\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 礼帽\n",
    "它的处理结果是输入图像和开运算之间的区别。下面的示例是针对9x9卷积核完成的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "kernel = np.ones((9, 9))\n",
    "tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(tophat),plt.title(\"tophat\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 黑帽\n",
    "它是输入图像闭运算和输入图像之间的差异。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)\n",
    "\n",
    "plt.subplot(121),plt.imshow(img),plt.title(\"src\"),plt.xticks([]),plt.yticks([])\n",
    "plt.subplot(122),plt.imshow(blackhat),plt.title(\"blackhat\"),plt.xticks([]),plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 结构元素\n",
    "我们在Numpy的帮助下手动创建了前面示例中的结构元素。它是正方形的，但在某些情况下可能需要椭圆或圆形卷积核。所以为此，OpenCV有一个函数cv.getStructuringElement()。只需传递卷积核的形状和大小，即可获得所需的卷积核。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv.getStructuringElement(cv.MORPH_RECT, (5, 5))  # 矩形\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))  # 椭圆\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))  # 十字\n"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "d001f9f6bbe9c72b73ec8f1147273505c2b9a823d50002fe074fa723ca99fac9"
  },
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
